# -*- coding: utf-8 -*-
#类和实例

import io
import sys
from types import MethodType

sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8')

# 使用__slots__要注意，__slots__定义的属性仅对当前类实例起作用，对继承的子类是不起作用的：
# 除非在子类中也定义__slots__，这样，子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。

class Student(object):
	__slots__ = ('name','age','setAge') #用tuple定义允许绑定的属性名称

s = Student()
s.name = 'Michael'
print(s.name)

def setAge(self,age):
	self.age = age

#给实例动态绑定方法
s.setAge = MethodType(setAge,s)
s.setAge(10)
print(s.age)

# s2 = Student()
# print(s2.age)

#给类动态绑定方法
Student.setAge = setAge
s3 = Student()
s3.setAge(20)
print(s3.age)


#限制类的属性测试
s = Student()
s.name = 'Michael' # 绑定属性'name'
s.age = 25 # 绑定属性'age'
# s.score=99
# print(s.score)


# @property广泛应用在类的定义中，可以让调用者写出简短的代码，同时保证对参数进行必要的检查，这样，程序运行时就减少了出错的可能性。
class Student(object):

	@property
	def score(self):
		return self._score

	@score.setter
	def score(self,value):
		self._score = value + 10 

s = Student()

s.score = 25
print(s.score)


# 多重继承
class Runnable(object):
    def run(self):
        print('Running...')

class Flyable(object):
    def fly(self):
        print('Flying...')

class Bird(Runnable,Flyable):
	pass

b = Bird()
b.run()
b.fly()


# 定制类
class Student(object):
	def __init__(self,name):
		self.name = name
	def __str__(self):
		return 'Student object (name: %s)' % self.name
	__repr__ = __str__

print(Student('Michael'))

s = Student('Tansia')
print(s)

# __iter__ 如果一个类想被用于for ... in循环，类似list或tuple那样，就必须实现一个__iter__()方法，
# 该方法返回一个迭代对象，然后，Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值，直到遇到StopIteration错误时退出循环。
# 要表现得像list那样按照下标取出元素，需要实现__getitem__()方法：

class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化两个计数器a，b

    def __iter__(self):
        return self # 实例本身就是迭代对象，故返回自己

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b # 计算下一个值
        if self.a > 100000: # 退出循环的条件
            raise StopIteration()
        return self.a # 返回下一个值
    def __getitem__(self, n):
        if isinstance(n,slice):
        	start = n.start
        	stop = n.stop
        	if start is None:
        		start = 0
        	a, b = 1, 1
        	L = []
        	for x in range(stop):
        		if x >= start:
        			L.append(a)
        		a , b = b , a + b
        	return L
        if isinstance(n,int):
        	a,b = 1,1
        	for x in range(n):
        		a,b = b,a+b
        	return a
for n in Fib():
	print(n)
f = Fib()
print(f[0],f[1],f[2])
print(f[:5])



# 当调用不存在的属性时，比如score，Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性
class Chain(object):
	def __init__(self,path=''):
		self._path = path
	def __getattr__(self,path):
		return Chain("%s/%s" % (self._path,path))
	def __str__(self):
		return self._path

	__repr__ = __str__

print(Chain().status.user.timeline.list)


# 任何类，只需要定义一个__call__()方法，
# 就可以直接对实例进行调用。

class Student(object):
	def __init__(self,name):
		self.name = name
	def __call__(self):
		print("My name is %s." % self.name)

s = Student('Michael')
s() # self参数不要传入

# 通过callable()函数，我们就可以判断一个对象是否是“可调用”对象

print(callable(Student('Nichael')))
print(callable(max))
print(callable([1,2,3]))
print(callable(None))